#!/usr/bin/env python3
"""
自适应缓冲区管理器
根据磁盘空间动态调整缓冲区大小和策略
"""

import time
import threading
from typing import Dict, Optional, Tuple, List, Callable
from collections import OrderedDict
from .disk_monitor import DiskMonitor, StorageLevel
from .storage_adjuster import StorageAdjuster, StorageConfig

class AdaptiveBufferFrame:
    """自适应缓冲区帧"""
    
    def __init__(self, table_name: str, page_id: int, page_data: bytearray):
        """初始化缓冲区帧"""
        self.table_name = table_name
        self.page_id = page_id
        self.page_data = page_data
        self.dirty = False
        self.pinned = False
        self.last_access = time.time()
        self.access_count = 0  # 访问次数
        self.priority = 1.0    # 优先级
    
    def mark_dirty(self):
        """标记为脏页"""
        self.dirty = True
    
    def pin(self):
        """固定页面"""
        self.pinned = True
    
    def unpin(self):
        """取消固定页面"""
        self.pinned = False
    
    def update_access_time(self):
        """更新访问时间"""
        self.last_access = time.time()
        self.access_count += 1
    
    def get_key(self) -> Tuple[str, int]:
        """获取页面的唯一标识"""
        return (self.table_name, self.page_id)
    
    def calculate_priority(self, current_time: float) -> float:
        """计算页面优先级"""
        # 基于访问频率和最近访问时间的优先级计算
        time_factor = 1.0 / (current_time - self.last_access + 1)
        frequency_factor = self.access_count
        return time_factor * frequency_factor

class AdaptiveBufferManager:
    """自适应缓冲区管理器"""
    
    def __init__(self, initial_buffer_size: int = 100, data_directory: str = "data"):
        """初始化自适应缓冲区管理器"""
        self.base_buffer_size = initial_buffer_size
        self.current_buffer_size = initial_buffer_size
        self.frames: Dict[Tuple[str, int], AdaptiveBufferFrame] = {}
        self.access_order = OrderedDict()
        self.storage_manager = None
        
        # 磁盘监控和存储调整器
        self.disk_monitor = DiskMonitor(data_directory)
        self.storage_adjuster = StorageAdjuster(data_directory)
        
        # 配置存储调整器
        config = StorageConfig(buffer_size=initial_buffer_size)
        self.storage_adjuster.update_base_config(config)
        
        # 注册回调
        self.storage_adjuster.register_callback('storage_adjusted', self._on_storage_adjusted)
        self.storage_adjuster.register_callback('auto_cleanup', self._on_auto_cleanup)
        
        # 统计信息
        self.stats = {
            'hits': 0,
            'misses': 0,
            'evictions': 0,
            'dirty_writes': 0,
            'total_requests': 0
        }
        
        # 自适应参数
        self.adaptive_params = {
            'hit_ratio_threshold': 0.8,  # 命中率阈值
            'eviction_threshold': 0.9,   # 驱逐阈值
            'priority_boost_factor': 1.2,  # 优先级提升因子
            'compression_threshold': 0.7   # 压缩阈值
        }
        
        # 线程锁
        self.lock = threading.RLock()
        
        # 启动监控
        self.storage_adjuster.start_monitoring(interval=30)
    
    def _on_storage_adjusted(self, data: Dict):
        """存储调整回调"""
        new_config = data['new_config']
        old_config = data['old_config']
        
        print(f"缓冲区大小调整: {old_config.buffer_size} -> {new_config.buffer_size}")
        
        # 调整缓冲区大小
        self._adjust_buffer_size(new_config.buffer_size)
        
        # 如果启用了压缩，对缓冲区中的页面进行压缩
        if new_config.compression_enabled and not old_config.compression_enabled:
            self._enable_compression()
        elif not new_config.compression_enabled and old_config.compression_enabled:
            self._disable_compression()
    
    def _on_auto_cleanup(self, data: Dict):
        """自动清理回调"""
        print("执行缓冲区自动清理...")
        self._perform_buffer_cleanup()
    
    def _adjust_buffer_size(self, new_size: int):
        """调整缓冲区大小"""
        with self.lock:
            if new_size < self.current_buffer_size:
                # 缩小缓冲区，需要驱逐一些页面
                self._evict_pages(self.current_buffer_size - new_size)
            
            self.current_buffer_size = new_size
            print(f"缓冲区大小已调整为: {self.current_buffer_size}")
    
    def _evict_pages(self, count: int):
        """驱逐页面"""
        if count <= 0:
            return
        
        # 按优先级排序，驱逐优先级最低的页面
        frames_by_priority = sorted(
            self.frames.values(),
            key=lambda f: f.calculate_priority(time.time())
        )
        
        evicted = 0
        for frame in frames_by_priority:
            if evicted >= count:
                break
            
            if not frame.pinned:
                self._evict_frame(frame)
                evicted += 1
                self.stats['evictions'] += 1
    
    def _evict_frame(self, frame: AdaptiveBufferFrame):
        """驱逐单个帧"""
        key = frame.get_key()
        
        # 如果是脏页，需要写回磁盘
        if frame.dirty and self.storage_manager:
            self.storage_manager.write_page(frame.page_id, bytes(frame.page_data))
            self.stats['dirty_writes'] += 1
        
        # 从缓冲区中移除
        del self.frames[key]
        if key in self.access_order:
            del self.access_order[key]
    
    def _perform_buffer_cleanup(self):
        """执行缓冲区清理"""
        with self.lock:
            # 清理长时间未访问的页面
            current_time = time.time()
            cleanup_threshold = 300  # 5分钟
            
            frames_to_remove = []
            for frame in self.frames.values():
                if (not frame.pinned and 
                    current_time - frame.last_access > cleanup_threshold):
                    frames_to_remove.append(frame)
            
            for frame in frames_to_remove:
                self._evict_frame(frame)
                self.stats['evictions'] += 1
            
            print(f"缓冲区清理完成，移除了 {len(frames_to_remove)} 个页面")
    
    def _enable_compression(self):
        """启用压缩"""
        print("启用缓冲区压缩...")
        # 这里可以实现页面压缩逻辑
        # 暂时只是标记
        pass
    
    def _disable_compression(self):
        """禁用压缩"""
        print("禁用缓冲区压缩...")
        # 这里可以实现解压缩逻辑
        pass
    
    def get_page(self, table_name: str, page_id: int) -> Optional[bytearray]:
        """获取页面"""
        with self.lock:
            key = (table_name, page_id)
            self.stats['total_requests'] += 1
            
            if key in self.frames:
                # 缓存命中
                frame = self.frames[key]
                frame.update_access_time()
                
                # 更新访问顺序
                if key in self.access_order:
                    del self.access_order[key]
                self.access_order[key] = frame
                
                self.stats['hits'] += 1
                return bytearray(frame.page_data)
            else:
                # 缓存未命中
                self.stats['misses'] += 1
                return None
    
    def put_page(self, table_name: str, page_id: int, page_data: bytearray, dirty: bool = False):
        """放入页面"""
        with self.lock:
            key = (table_name, page_id)
            
            # 如果页面已存在，更新它
            if key in self.frames:
                frame = self.frames[key]
                frame.page_data = bytearray(page_data)
                frame.update_access_time()
                if dirty:
                    frame.mark_dirty()
                return
            
            # 检查缓冲区是否已满
            if len(self.frames) >= self.current_buffer_size:
                self._evict_lru_page()
            
            # 创建新帧
            frame = AdaptiveBufferFrame(table_name, page_id, bytearray(page_data))
            if dirty:
                frame.mark_dirty()
            
            self.frames[key] = frame
            self.access_order[key] = frame
    
    def _evict_lru_page(self):
        """驱逐最近最少使用的页面"""
        if not self.access_order:
            return
        
        # 获取最旧的页面
        key, frame = self.access_order.popitem(last=False)
        
        # 如果是脏页，写回磁盘
        if frame.dirty and self.storage_manager:
            self.storage_manager.write_page(frame.page_id, bytes(frame.page_data))
            self.stats['dirty_writes'] += 1
        
        # 从缓冲区中移除
        del self.frames[key]
        self.stats['evictions'] += 1
    
    def pin_page(self, table_name: str, page_id: int):
        """固定页面"""
        with self.lock:
            key = (table_name, page_id)
            if key in self.frames:
                self.frames[key].pin()
    
    def unpin_page(self, table_name: str, page_id: int):
        """取消固定页面"""
        with self.lock:
            key = (table_name, page_id)
            if key in self.frames:
                self.frames[key].unpin()
    
    def mark_dirty(self, table_name: str, page_id: int):
        """标记页面为脏页"""
        with self.lock:
            key = (table_name, page_id)
            if key in self.frames:
                self.frames[key].mark_dirty()
    
    def flush_all(self):
        """刷新所有脏页到磁盘"""
        with self.lock:
            if not self.storage_manager:
                return
            
            dirty_count = 0
            for frame in self.frames.values():
                if frame.dirty:
                    self.storage_manager.write_page(frame.page_id, bytes(frame.page_data))
                    frame.dirty = False
                    dirty_count += 1
            
            self.stats['dirty_writes'] += dirty_count
            print(f"刷新了 {dirty_count} 个脏页到磁盘")
    
    def get_stats(self) -> Dict[str, any]:
        """获取统计信息"""
        with self.lock:
            hit_ratio = (self.stats['hits'] / self.stats['total_requests'] 
                        if self.stats['total_requests'] > 0 else 0)
            
            return {
                'buffer_size': self.current_buffer_size,
                'used_frames': len(self.frames),
                'hit_ratio': hit_ratio,
                'hits': self.stats['hits'],
                'misses': self.stats['misses'],
                'evictions': self.stats['evictions'],
                'dirty_writes': self.stats['dirty_writes'],
                'total_requests': self.stats['total_requests']
            }
    
    def get_storage_status(self) -> Dict[str, any]:
        """获取存储状态"""
        return self.storage_adjuster.get_storage_status()
    
    def print_status(self):
        """打印状态"""
        stats = self.get_stats()
        storage_status = self.get_storage_status()
        
        print("=" * 60)
        print("自适应缓冲区状态")
        print("=" * 60)
        print(f"缓冲区大小: {stats['buffer_size']}")
        print(f"已使用帧数: {stats['used_frames']}")
        print(f"命中率: {stats['hit_ratio']:.2%}")
        print(f"命中次数: {stats['hits']}")
        print(f"未命中次数: {stats['misses']}")
        print(f"驱逐次数: {stats['evictions']}")
        print(f"脏页写入次数: {stats['dirty_writes']}")
        print(f"总请求次数: {stats['total_requests']}")
        print()
        
        # 存储状态
        disk_info = storage_status['disk_info']
        print("存储状态:")
        print(f"  磁盘使用率: {disk_info['percentage']:.1f}%")
        print(f"  存储级别: {disk_info['level']}")
        print(f"  可用空间: {self.disk_monitor.format_size(disk_info['free'])}")
        print()
        
        # 当前配置
        config = storage_status['current_config']
        print("当前配置:")
        print(f"  缓冲区大小: {config['buffer_size']}")
        print(f"  压缩启用: {'是' if config['compression_enabled'] else '否'}")
        print(f"  自动清理: {'是' if config['auto_cleanup'] else '否'}")
    
    def force_cleanup(self):
        """强制清理"""
        self._perform_buffer_cleanup()
        return self.storage_adjuster.force_cleanup()
    
    def set_storage_manager(self, storage_manager):
        """设置存储管理器"""
        self.storage_manager = storage_manager
    
    def shutdown(self):
        """关闭缓冲区管理器"""
        print("关闭自适应缓冲区管理器...")
        
        # 刷新所有脏页
        self.flush_all()
        
        # 停止监控
        self.storage_adjuster.stop_monitoring()
        
        print("自适应缓冲区管理器已关闭")